#include "idProducer.h"


IdProducer* IdProducer::producer = new IdProducer(1694162025198, 0, 0);

IdProducer::IdProducer(uint64 beginTime, uint64 dataCenterId, uint64 workerId) : datacenterId(dataCenterId), workerId(workerId) {
	if (beginTime == -1) {
		this->startTime = timeGen();
	} else {
		this->startTime = beginTime;
	}
	//检测计算机ID是否满足条件
	if (workerId > maxWorkerId || workerId < 0) {
		std::stringstream fmt;
		fmt << "worker Id can't be greater than " << maxWorkerId << " or less than 0";
		throw exception(fmt.str().c_str());
	}
	//检验数据中心的编号是否满足条件
	if (datacenterId > maxDatacenterId || datacenterId < 0) {
		std::stringstream fmt;
		fmt << "datacenterId Id can't be greater than " << maxDatacenterId << " or less than 0";
		throw exception(fmt.str().c_str());
	}
}

uint64 IdProducer::getNextId() {
	std::unique_lock<std::mutex> lock{ mutex };
	//获取时间戳
	uint64 timestamp = timeGen();
	//如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过这个时候应该抛出异常
	if (timestamp < lastTimestamp) {
		std::stringstream fmt;
		fmt << "Clock moved backwards.  Refusing to generate id for " << lastTimestamp - timestamp << " milliseconds";
		throw exception(fmt.str().c_str());
	}
	//如果是统一时间生成的，则进行毫秒内序列
	if (timestamp == lastTimestamp) {
		//如果毫秒相同，则从0递增生成序列号
		sequence = (sequence + 1) & sequenceMask;
		//毫秒内序列溢出
		if (sequence == 0) {
			//阻塞到下一个毫秒，获得新的时间戳
			timestamp = tilNextSpan(lastTimestamp);
		}
	} else {
		sequence = 0L;
	}
	//上次生成ID的时间戳
	lastTimestamp = timestamp;
	//移位并通过运算拼接到一起组成64位的ID
	uint64 id = ((timestamp - startTime) << timestampLeftShift) | (workerId << workerIdShift) | sequence;
	return id;
}

uint64 IdProducer::nextId() {
	if (producer != nullptr) {
		return producer->getNextId();
	} else {
		throw exception("None ID Producer...");
	}
}

uint64 IdProducer::timeGen() {
	std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> tp = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
	auto tmp = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch());
	std::time_t timestamp = tmp.count();
	//std::time_t timestamp = std::chrono::system_clock::to_time_t(tp);
	return timestamp;
}

uint64 IdProducer::tilNextSpan(uint64 lastTimestamp) {
	uint64 timestamp = timeGen();
	while (timestamp <= lastTimestamp) {
		timestamp = timeGen();
	}
	return timestamp;
}
